#ifndef __M_DB_HPP__
#define __M_DB_HPP__

#include <iostream>
#include <mutex>
#include <memory>
#include <cstdio>
#include <cassert>
#include "util.hpp"

/*定义一个对于表操作的类，这样子到时候我们要访问哪张表，使用哪个类实例化的对象即可*/
class user_table
{
public:
    user_table(const std::string &host, const std::string &user, const std::string &password,
               const std::string &db, uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host, user, password, db, port);
        assert(_mysql);
    }
    ~user_table()
    {
        if (_mysql != nullptr)
        {
            mysql_util::mysql_destroy(_mysql);
        }
    }

    // 注册用户
    bool insert(const Json::Value &user)
    {
        // 验证user的合法性
        if (user["username"].isNull() || user["password"].isNull())
        {
            DLOG("insert password or username");
            return false;
        }
        // 定义插入匹配宏
#define INSERT_USER "insert into user values(null,'%s',password('%s'),1000,0,0);"
        // 插入执行
        char buffer[4096];
        bzero(buffer, sizeof(buffer));
        snprintf(buffer, sizeof(buffer) - 1, INSERT_USER, user["username"].asCString(), user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, buffer);
        if (ret == false)
        {
            DLOG("insert user info failed!!!");
            return false;
        }
        return true;
    }

    // 登录验证，并返回用户信息
    bool login(Json::Value &user)
    {
        // 验证user的合法性
        if (user["username"].isNull() || user["password"].isNull())
        {
            DLOG("insert password or username");
            return false;
        }

        // 执行SQL语句
#define LOGIN_USER "select id,score,total_count,win_count from user where username='%s' and password=password('%s');"
        char buffer[4096];
        bzero(buffer, sizeof(buffer));
        snprintf(buffer, sizeof(buffer) - 1, LOGIN_USER, user["username"].asCString(), user["password"].asCString());
        MYSQL_RES *res = nullptr;
        {
            /*mysql_query和mysql_store_result两个函数本身在MySQL中是保证线程安全的
            但是由于user_table可能会被多个线程用到，不保证线程安全，防止mysql_exec执行完之后有数据，但是在执行mysql_store_result之前
            其他线程有执行了mysql_exec将数据覆盖，有线程安全问题，特意加锁保护
            */
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, buffer);
            if (ret == false)
            {
                DLOG("user login failed!!!");
                return false;
            }

            // 走到这里是会有数据的，查找到的数据也是只有一条数据
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("have no login user info!!!");
                return false;
            }
        }
        int num_row = mysql_num_rows(res);
        if (num_row != 1)
        {
            DLOG("the user information queried is not unique!!!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    // 通过用户名获取用户信息
    bool select_by_name(const std::string &name, Json::Value &user)
    {
#define USER_BY_NAME "select id,score,total_count,win_count from user where username='%s';"
        // SQL语句执行
        char buffer[4096];
        bzero(buffer, sizeof(buffer));
        snprintf(buffer, sizeof(buffer) - 1, USER_BY_NAME, name.c_str());
        MYSQL_RES *res = nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, buffer);
            if (ret == false)
            {
                DLOG("get user by name failed!!!");
                return false;
            }

            // 走到这里证明是有数据的，按理来说数据也只有一条
            res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                DLOG("have no user info!!!");
                return false;
            }
        }
        //获取数据
        int num_row = mysql_num_rows(res);
        if (num_row != 1)
        {
            DLOG("the user information queried is not unique!!!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"]=(Json::UInt64)std::stol(row[0]);
        user["username"]=name;
        user["score"]=(Json::UInt64)std::stol(row[1]);
        user["total_count"]=std::stoi(row[2]);
        user["win_count"]=std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }

    // 通过用户ID获取用户信息
    bool select_by_id(uint64_t id, Json::Value &user)
    {
#define USER_BY_ID "select username,score,total_count,win_count from user where id=%d;"
        char buffer[4096];
        bzero(buffer,sizeof(buffer));
        snprintf(buffer,sizeof(buffer)-1,USER_BY_ID,id);
        //执行SQL语句
        MYSQL_RES* res=nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret=mysql_util::mysql_exec(_mysql,buffer);
            if(ret==false)
            {
                DLOG("get user id failed!!!");
                return false;
            }
            res=mysql_store_result(_mysql);
            if(res==nullptr)
            {
                DLOG("have no user info!!!");
                return false;
            }
        }
        //获取数据
        int num_row=mysql_num_rows(res);
        if(num_row!=1)
        {
            DLOG("the user information queried is not unique!!!");
            return false;
        }
        MYSQL_ROW row=mysql_fetch_row(res);
        user["id"]=(Json::UInt64)id;
        user["username"]=row[0];
        user["score"]=(Json::UInt64)std::stol(row[1]);
        user["total_count"]=std::stoi(row[2]);
        user["win_count"]=std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    // 胜利时天梯分数增加30分，战斗总场次增加1，战斗胜利场次增加1
    bool win(uint64_t id)
    {
#define USER_WIN "update user set score=score+30,total_count=total_count+1,win_count=win_count+1 where id=%d;"
        char buffer[4096];
        bzero(buffer,sizeof(buffer));
        snprintf(buffer,sizeof(buffer)-1,USER_WIN,id);
        //执行SQL语句
        bool ret=mysql_util::mysql_exec(_mysql,buffer);
        if(ret==false)
        {
            DLOG("update win user info failed!!!");
            return false;
        }
        return true;
    }
    // 失败时天梯分数减少30分，战斗总场次增加1，其他不变
    bool lose(uint64_t id)
    {
#define USER_LOSE "update user set score=score-30,total_count=total_count+1 where id=%d;"
        char buffer[4096];
        bzero(buffer,sizeof(buffer));
        snprintf(buffer,sizeof(buffer)-1,USER_LOSE,id);
        bool ret=mysql_util::mysql_exec(_mysql,buffer);
        if(ret==false)
        {
            DLOG("update lose user info failed!!!");
            return false;
        }
        return true;
    }

private:
    MYSQL *_mysql = nullptr; // 操作数据库的句柄
    std::mutex _mutex;       // 操作数据库时加锁保护
};

#endif